package utilities;


/**
 * 
 * @author Mathieu Rousseau
 *
 * @email mathieu.rousseau3@mail.mcgill.ca
 *
**/


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;


/** ------------------------------------------------------------------------------------------------------------------
 * start of LOOCV class
 * ------------------------------------------------------------------------------------------------------------------  **/
public class LOOCV {

	
	/*  global variables  */
	private static String inputPath = "LOOCV/Input/";
	private static String outputPath = "LOOCV/Output/";
	private static double exponent;
	private static int numberOfFoldersAndFiles;
	/*  end of global variables  */
	
	
	/*  start of getValidationValues method  */
	private static void getValidationValues(String filename, HashMap<Integer, ArrayList<Double>> validationValuesHashMap){
		
		String line = null;
		
		try{
			BufferedReader br = new BufferedReader(new FileReader(inputPath + filename));			
			while((line = br.readLine()) != null){
				String[] values = line.split("\t");
				if(values.length == 6){
//					System.out.println(line);
					
					Integer indexLOOCV = Integer.parseInt(values[0]);
					
					Double fragmentOneIndex = Double.parseDouble(values[1]);
					Double fragmentTwoIndex = Double.parseDouble(values[2]);
					Double IF = Double.parseDouble(values[3]);
					Double stdev = Double.parseDouble(values[4]);
					Double count = Double.parseDouble(values[5]);
					
					ArrayList<Double> newEntry = new ArrayList<Double>(5);
					newEntry.add(fragmentOneIndex);
					newEntry.add(fragmentTwoIndex);
					newEntry.add(IF);
					newEntry.add(stdev);
					newEntry.add(count);
					newEntry.trimToSize();
					
					validationValuesHashMap.put(indexLOOCV, newEntry);
				}
				else{
					//  each row must have in order: index    fragment1    fragment2    IF    stdev    count
					System.out.println("Error: there are not six (6) entries in each row of the LOOCV validation file\n"
							+ "System.exit(0)");
					System.exit(0);
				}
			}
			br.close();
		}catch (IOException e) {
			e.printStackTrace();
			System.out.println("Error: cannot find LOOCV validation file with filename \"" + filename + "\"\n" +
			"System.exit(0)");
			System.exit(0);
		}
	}/*  end of getValidationValues method  */
	
	
	/*  start of main method  */
	public static void main(String[] args) {
		
		System.out.println("Calculating total SSE from LOOCV experiment...");
		
		
		
		//  set the exponent being looked at for the current run
		exponent = 4.00;
		
		
		
		
		//  set the total number of folders and files that should exist in the input folder
		numberOfFoldersAndFiles = 380;

		
		
		
		//  get the validation values that were left out
		String filenameValidationValues = "LOOCV_validation_DMSO_experimental.txt";
		
		HashMap<Integer, ArrayList<Double>> validationValues = new HashMap<Integer, ArrayList<Double>>();
		getValidationValues(filenameValidationValues, validationValues);
		
		Iterator<Integer> keyIterator = validationValues.keySet().iterator();
		
		while(keyIterator.hasNext()){
			Integer currentKey = keyIterator.next();
//			System.out.println(currentKey);
			
			ArrayList<Double> currentEntry = validationValues.get(currentKey);
			
//			System.out.println(currentKey + ": " + currentEntry.size());
			if(currentEntry.size() != 5){
				System.out.println("Error: the stored validation values does not have five (5) entries in each row.\nSystem.exit(0)");
				System.exit(0);
			}				
		}
		
		
		double totalError = 0;
		
		
		HashMap<Integer, String> bestStructures = new HashMap<Integer, String>();
		
		
		File inputFolder = new File(inputPath);
		String[] folderList = inputFolder.list();
		
		//  make sure that there are exactly 380 files in the input folder
		//  there should be 379 folders for the LOOCV runs and one file that contains the missing values used for validation
		int folderListLength = folderList.length;
		for(String currentFolderName: folderList){
			if(currentFolderName.equals(".DS_Store")){
				folderListLength--;
			}
		}
		if(folderListLength != numberOfFoldersAndFiles){
			System.out.println("Error: there are " + folderListLength + " folders which is not exactly " + numberOfFoldersAndFiles + " files.\nSystem.exit(0)");
			System.exit(0);
		}
		
		for(String currentFolderName: folderList){
//			System.out.println(currentFolderName);
			
			File currentFolder = new File(inputPath + currentFolderName);
			
			if(currentFolder.isFile()){
//				System.out.println("Found a file named \"" + currentFolderName + "\" and skipping.");
				continue;
			}
			
			String[] fileList = currentFolder.list();
			
			if(fileList.length != 5){
				if(fileList.length == 6){
					if(fileList[0].equals(".DS_Store")){
						//  ok, do not exit
					}
					else{
						System.out.println("Error: the folder \"" + currentFolderName + "\" does not have the right number of files.\nSystem.exit(0)");
						System.exit(0);
					}
				}
				else{
					System.out.println("Error: the folder \"" + currentFolderName + "\" does not have the right number of files.\nSystem.exit(0)");
					System.exit(0);
				}
			}
			//  else case is that there are exactly five (5) files (or six (6) with the .DS_store file
			//  continue without error
			
			
			for(String currentFileName: fileList){
//				System.out.println(currentFileName);
				
				if(currentFileName.startsWith("best_structure_")){
					String[] parsedFileName = currentFileName.split("_");
					
					int LOOCVindex = Integer.parseInt(parsedFileName[2]);
//					System.out.println(LOOCVindex);
					
					bestStructures.put(LOOCVindex, currentFolder + "/" + currentFileName);
				}
			}
		}
		
		
		PDButility pdbTool = new PDButility();
		
		keyIterator = bestStructures.keySet().iterator();
		
		while(keyIterator.hasNext()){
			Integer currentKey = keyIterator.next();
//			System.out.println(currentKey);
//			indices.add(currentKey);
			
			String currentBestStructureFilename = bestStructures.get(currentKey);			
//			System.out.println(currentKey + ": " + currentBestStructureFilename);
			
			double[][] currentStructure = pdbTool.getStructure(currentBestStructureFilename);
//			System.out.println(currentStructure[0][0] + "\t" + currentStructure[0][1] + "\t" + currentStructure[0][2]);
			
			ArrayList<Double> leftOutValue = validationValues.get(currentKey);
			int leftOutFragmentOne = leftOutValue.get(0).intValue() - 1;		//  set the indices to start at zero to be able to index into the array
			int leftOutFragmentTwo = leftOutValue.get(1).intValue() - 1;
			double leftOutIF = leftOutValue.get(2);
			
			double[] fragmentOneCoordinates = new double[3];
			double[] fragmentTwoCoordinates = new double[3];
			
			fragmentOneCoordinates[0] = currentStructure[leftOutFragmentOne][0];
			fragmentOneCoordinates[1] = currentStructure[leftOutFragmentOne][1];
			fragmentOneCoordinates[2] = currentStructure[leftOutFragmentOne][2];
			
			fragmentTwoCoordinates[0] = currentStructure[leftOutFragmentTwo][0];
			fragmentTwoCoordinates[1] = currentStructure[leftOutFragmentTwo][1];
			fragmentTwoCoordinates[2] = currentStructure[leftOutFragmentTwo][2];
			
			
			double distanceInStructure = 0;
			double tempTerm = fragmentTwoCoordinates[0] - fragmentOneCoordinates[0];		//   x2 - x1
			distanceInStructure += tempTerm * tempTerm;										//  (x2 - x1)^2
			tempTerm = fragmentTwoCoordinates[1] - fragmentOneCoordinates[1];				//   y2 - y1
			distanceInStructure += tempTerm * tempTerm;										//  (y2 - y1)^2
			tempTerm = fragmentTwoCoordinates[2] - fragmentOneCoordinates[2];				//   z2 - z1
			distanceInStructure += tempTerm * tempTerm;										//  (z2 - z1)^2
			distanceInStructure = Math.sqrt(distanceInStructure);
//			System.out.println(distanceInStructure);
			
			double IFcalculatedFromStructure = Math.pow(distanceInStructure, (-1.0)*exponent);
			
			double error = leftOutIF - IFcalculatedFromStructure;
			error *= error;
			totalError += error;
			
//			if(currentKey == 1){
//				System.out.println("\n");
//				System.out.println("fileName=\t" + currentBestStructureFilename);
//				System.out.println("LOOCVindex=\t" + currentKey);
//				System.out.println((leftOutFragmentOne+1) + ":\t" + fragmentOneCoordinates[0] + "\t" + fragmentOneCoordinates[1] + "\t" + fragmentOneCoordinates[2]);
//				System.out.println((leftOutFragmentTwo+1) + ":\t" + fragmentTwoCoordinates[0] + "\t" + fragmentTwoCoordinates[1] + "\t" + fragmentTwoCoordinates[2]);
//				System.out.println("distanceInStructure=\t" + distanceInStructure);
//				System.out.println("IFcalculatedFromStructure=\t" + IFcalculatedFromStructure);
//				System.out.println("leftOutIF=\t" + leftOutIF);
//				System.out.println("error=\t" + error);
//				System.out.println("\n");
//			}
			
//			System.out.println(leftOutFragmentOne + "-" + leftOutFragmentTwo + ":\t" + leftOutIF + "\t" + IFcalculatedFromStructure + "\t" + error);
		}
		
//		System.out.println(exponent + ": totalError = " + totalError);
		
		String outputFilename = outputPath + exponent + "_totalError.txt";		
		try{						
			BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilename));
			
			bw.write("exponent=\t" + exponent);
			bw.newLine();
			bw.flush();
			
			bw.write("total error=\t" + totalError);
			bw.flush();
			
			//  close the writer			
			bw.close();
		}catch (IOException e) {
			System.out.println("Error: could not output file: " + outputFilename);
			e.printStackTrace();
		}//end of try-catch block
		System.out.println("Output file: " + outputFilename);
		
		System.out.println("\nDone!");
	}/*  end of main method  */
}/*  end of LOOCV class  */
